﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using XmlDSigEx;
using System.Xml;
using System.Security.Cryptography.Xml;

namespace TestXmlDSigEx
{
    /// <summary>
    /// Summary description for TestCanonicalisation
    /// </summary>
    [TestClass]
    public class TestCanonicalisation
    {
        public TestCanonicalisation()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void Scenario1()
        {
            string input = @"<?xml version=""1.0""?>

<?xml-stylesheet   href=""doc.xsl""
   type=""text/xsl""   ?>

<!DOCTYPE doc SYSTEM ""doc.dtd"">

<doc>Hello, world!<!-- Comment 1 --></doc>

<?pi-without-data     ?>

<!-- Comment 2 -->

<!-- Comment 3 -->";
            string expectOutputWithoutComments = "<?xml-stylesheet href=\"doc.xsl\"\n   type=\"text/xsl\"   ?>\n<doc>Hello, world!</doc>\n<?pi-without-data?>";

            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessOctetStream(inputStream, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }

        [TestMethod]
        public void Scenario2()
        {
            string input = @"<doc>
   <clean>   </clean> 
   <dirty>   A   B   </dirty>
   <mixed>
      A
      <clean>   </clean>
      B
      <dirty>   A   B   </dirty>
      C
   </mixed>
</doc>";
            string expectOutputWithoutComments = "<doc>\n   <clean>   </clean> \n   <dirty>   A   B   </dirty>\n   <mixed>\n      A\n      <clean>   </clean>\n      B\n      <dirty>   A   B   </dirty>\n      C\n   </mixed>\n</doc>";
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessOctetStream(inputStream, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }

        [TestMethod]
        public void Scenario3()
        {
            string input = @"<!DOCTYPE doc [<!ATTLIST e9 attr CDATA ""default"">]>
<doc>
   <e1   />
   <e2   ></e2>
   <e3   name = ""elem3""   id=""elem3""   />
   <e4   name=""elem4""   id=""elem4""   ></e4>
   <e5 a:attr=""out"" b:attr=""sorted"" attr2=""all"" attr=""I'm""
      xmlns:b=""http://www.ietf.org""
      xmlns:a=""http://www.w3.org""
      xmlns=""http://example.org""/>
   <e6 xmlns="""" xmlns:a=""http://www.w3.org"">
      <e7 xmlns=""http://www.ietf.org"">
         <e8 xmlns="""" xmlns:a=""http://www.w3.org"">
            <e9 xmlns="""" xmlns:a=""http://www.ietf.org""/>
         </e8>
      </e7>
   </e6>
</doc>";
            string expectOutputWithoutComments = "<doc>\n   <e1></e1>\n   <e2></e2>\n   <e3 id=\"elem3\" name=\"elem3\"></e3>\n   <e4 id=\"elem4\" name=\"elem4\"></e4>\n   <e5 xmlns=\"http://example.org\" xmlns:a=\"http://www.w3.org\" xmlns:b=\"http://www.ietf.org\" attr=\"I'm\" attr2=\"all\" b:attr=\"sorted\" a:attr=\"out\"></e5>\n   <e6 xmlns:a=\"http://www.w3.org\">\n      <e7 xmlns=\"http://www.ietf.org\">\n         <e8 xmlns=\"\">\n            <e9 xmlns:a=\"http://www.ietf.org\" attr=\"default\"></e9>\n         </e8>\n      </e7>\n   </e6>\n</doc>";
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessOctetStream(inputStream, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }

        [TestMethod]
        public void Scenario4()
        {
            string input = @"<!DOCTYPE doc [
<!ATTLIST normId id ID #IMPLIED>
<!ATTLIST normNames attr NMTOKENS #IMPLIED>
]>
<doc>
   <text>First line&#x0d;&#10;Second line</text>
   <value>&#x32;</value>
   <compute><![CDATA[value>""0"" && value<""10"" ?""valid"":""error""]]></compute>
   <compute expr='value>""0"" &amp;&amp; value&lt;""10"" ?""valid"":""error""'>valid</compute>
   <norm attr=' &apos;   &#x20;&#13;&#xa;&#9;   &apos; '/>
   <normNames attr='   A   &#x20;&#13;&#xa;&#9;   B   '/>
   <normId id=' &apos;   &#x20;&#13;&#xa;&#9;   &apos; '/>
</doc>";
            string expectOutputWithoutComments = "<doc>\n   <text>First line&#xD;\nSecond line</text>\n   <value>2</value>\n   <compute>value&gt;\"0\" &amp;&amp; value&lt;\"10\" ?\"valid\":\"error\"</compute>\n   <compute expr=\"value>&quot;0&quot; &amp;&amp; value&lt;&quot;10&quot; ?&quot;valid&quot;:&quot;error&quot;\">valid</compute>\n   <norm attr=\" '    &#xD;&#xA;&#x9;   ' \"></norm>\n   <normNames attr=\"A &#xD;&#xA;&#x9; B\"></normNames>\n   <normId id=\"' &#xD;&#xA;&#x9; '\"></normId>\n</doc>";
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessOctetStream(inputStream, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }

        //[TestMethod(]
        public void Scenario5()
        {
            string input = @"<?xml version=""1.0"" encoding=""ISO-8859-1""?>
<doc>&#169;</doc>";
            string expectOutputWithoutComments = "<doc>\u00C2\u00A9</doc>";
            
            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessOctetStream(inputStream, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }
        [TestMethod]
        public void Scenario6()
        {
            string input = @"<!DOCTYPE doc [ 
<!ATTLIST e2 xml:space (default|preserve) 'preserve'> 
<!ATTLIST e3 id ID #IMPLIED> 
]> 
<doc xmlns=""http://www.ietf.org"" xmlns:w3c=""http://www.w3.org""> 
   <e1> 
      <e2 xmlns=""""> 
         <e3 id=""E3""/> 
      </e2> 
   </e1> 
</doc>";
            string expectOutputWithoutComments = "<e1 xmlns=\"http://www.ietf.org\" xmlns:w3c=\"http://www.w3.org\"><e3 xmlns=\"\" id=\"E3\" xml:space=\"preserve\"></e3></e1>";

            byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(input);
            MemoryStream inputStream = new MemoryStream(inputBytes);

            // This tests the XML node set functionality of the canonicalisation algorithm directly.
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ProhibitDtd = false;
            //settings.XmlResolver = null;

            XmlReader reader = XmlReader.Create(inputStream, settings);
            XmlDocument document = new XmlDocument() { PreserveWhitespace = true };
            document.Load(reader);

            string xPath = @"(//*[local-name() = 'e1' or local-name() ='e3'] | //@* | //namespace::*)";

            XmlNamespaceManager nMgr = new XmlNamespaceManager(document.NameTable);
            nMgr.AddNamespace("ietf", "http://www.ietf.org");
            XmlNodeList nodeSet  = document.SelectNodes(xPath, nMgr);
            ICanonicalisationAlgorithm canon = new XmlC14NCanonicalisation();

            Stream outputStream = canon.ProcessNodeSet(nodeSet, false);
            StreamReader outputStreamReader = new StreamReader(outputStream, UTF8Encoding.UTF8);
            string output = outputStreamReader.ReadToEnd();

            Assert.AreEqual(expectOutputWithoutComments, output);
        }
    }
}
